// 给watch增加newval和oldval
let activeEffect
const data = {num: 1, num2: 2, num3: 3, num4: 4}
const bucket = new WeakMap()
let effectStack = []

// 收集依赖
function track (target, key) {
  let depsMap = bucket.get(target)
  if (!depsMap) {
    bucket.set(target, depsMap = new Map())
  }
  let deps = depsMap.get(key)
  if (!deps) {
    depsMap.set(key, deps = new Set())
  }
  if (activeEffect) {
    deps.add(activeEffect)
    activeEffect.deps.push(deps)
  }
}

// 触发事件
function trigger (target, key) {
  const depsMap = bucket.get(target)
  if (!depsMap) return
  const effects = depsMap.get(key)
  const effectsToRun = new Set()
  effects.forEach(effect => {
    if (effect !== activeEffect) {
      effectsToRun.add(effect)
    }
  })

  effectsToRun.forEach(effect => {
    if (effect.options.schedule) {
      effect.options.schedule(effect)
    } else {
      effect()
    }
  })
}

// 代理
const obj = new Proxy(data, {
  get(target, key) {
    track(target, key)
    return target[key]
  },
  set(target, key, value) {
    target[key] = value
    trigger(target, key)
  }
})

function cleanup (effectFn) {
  for (let i = 0; i < effectFn.deps.length; i++) {
    const deps = effectFn.deps[i]
    deps.delete(effectFn)
  }
  effectFn.deps.length = 0
}

function effect (fn, options = {}) {
  const effectFn = () => {
    cleanup(effectFn)
    activeEffect = effectFn
    effectStack.push(effectFn)
    const res = fn()
    effectStack.pop()
    activeEffect = effectStack[effectStack.length - 1]
    // 返回
    return res
  }
  effectFn.deps = []
  effectFn.options = options
  if(!options.lazy) {
    effectFn()
  }
  return effectFn
}

// 使用traverse函数将每个属性都堵了一遍
function traverse (value, seen=new Set()) {
  if (typeof value !== 'object' || seen.has(value)) return
  seen.add(value)
  for (const key in value) {
    traverse(value[key], seen)
  }
  return value
}

function watch (source, cb) {
  // 新增, 兼容source为对象的情况
  let getter
  if (typeof source === 'function') {
    getter = source
  } else if (typeof source === 'object') {
    getter = () => traverse(source)
  }

  // 新旧值
  let newval, oldval
  const effectFn = effect(() => getter(), {
    lazy: true,
    schedule() {
      newval = effectFn()
      cb(newval, oldval)
      oldval = newval
    }
  })
  oldval = effectFn()
  console.log(oldval, 'oldval')
}

watch(() => obj.num, (newval, oldval) => {
  console.log('改了!', newval, oldval)
})

obj.num = 123
obj.num = 234

/**
 * 解析: 核心还是在使用effect函数, 利用了schedule调度方法, 让cb在更新的时候执行
 * 注意, 此时effect原本监听的副作用函数(() => traverse(source))反而是不执行了;
 * 反而是执行watch的回调, 相当于用watch的回调代替掉了effect原本监听的那个回调
 */



